package com.general.framework.data.mybatis;

import com.general.framework.core.exception.SystemErrorException;
import com.general.framework.core.lang.Lists;
import com.general.framework.core.schema.PageRequest;
import com.general.framework.core.schema.PageResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.ibatis.session.RowBounds;
import javax.annotation.Resource;

import tk.mybatis.mapper.entity.Example;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

@Slf4j
public abstract class BaseRepository<M extends MybatisMapper<T, PK>, T, PK> {

    protected M mapper;

    @Resource
    public void setMapper(final M mapper) {
        this.mapper = mapper;
    }

    /**
     * 泛型entity类
     */
    private Class<T> entityClass;

    /**
     * 插入对象
     * @param entity 实体值
     * @return 插入操作记录数
     */
    public int insert(final T entity) {
        this.preInsert(entity);
        return this.mapper.insert(entity);
    }

    /**
     * 插入对象，没有值的对象不会插入
     * @param entity 实体值
     * @return 插入操作记录数
     */
    public int insertSelective(final T entity) {
        this.preInsert(entity);
        return this.mapper.insertSelective(entity);
    }

    /**
     * 批量增加数据到数据库
     * @param entities
     * @return
     */
    public int insertList(final List<T> entities) {
        if (CollectionUtils.isEmpty(entities)) {
            return 0;
        }
        entities.forEach((entity) -> this.preInsert(entity));
        return this.mapper.insertList(entities);
    }

    /**
     * 根据主键更新实体全部字段，null值会被更新
     * @param entity 对象
     * @return 插入操作记录数
     */
    public int updateByPrimaryKey(final T entity) {
        preUpdate(entity);
        return this.mapper.updateByPrimaryKey(entity);
    }

    /**
     * 根据主键更新属性不为null的值
     * @param entity 对象
     * @return 插入操作记录数
     */
    public int updateByPrimaryKeySelective(final T entity) {
        preUpdate(entity);
        return this.mapper.updateByPrimaryKeySelective(entity);
    }

    /**
     * 根据条件更新
     * @param record
     * @param example
     * @return
     */
    protected int updateBySelective(final T record, final Example example) {
        preUpdate(record);
        example.setForUpdate(true);
        return this.mapper.updateByExampleSelective(record, example);
    }

    /**
     * 根据主键字段进行删除，方法参数必须包含完整的主键属性
     * @param primaryKey 主键参数值
     * @return 删除操作记录数
     */
    public int deleteByPrimaryKey(final PK primaryKey) {
        if (primaryKey == null) {
            return 0;
        }
        return this.mapper.deleteByPrimaryKey(primaryKey);
    }

    /**
     * 根据唯一主键ID的集合删除实体
     * @param anIdList
     * @return
     */
    public int deleteByIdList(final List<PK> anIdList) {
        if (Lists.isEmpty(anIdList)) {
            return 0;
        }
        return this.mapper.deleteByIdList(anIdList);
    }

    /**
     * 根据实体主键查询实体，查询条件使用等号
     * @param primaryKey 主键值
     * @return 查询结果
     */
    public T selectByPrimaryKey(final PK primaryKey) {
        if (primaryKey == null) {
            return null;
        }
        return this.mapper.selectByPrimaryKey(primaryKey);
    }

    /**
     * 根据唯一主键ID 的集合查询实体
     * @param anIdList
     * @return
     */
    public List<T> selectByPrimaryKeyList(final List<PK> anIdList) {
        if (Lists.isEmpty(anIdList)) {
            return new ArrayList<>();
        }
        return this.mapper.selectByIdList(anIdList);
    }

    /**
     * 根据主键查找数据是否存在，主要用于防御性编程和幂等
     * @param anKey
     * @return
     */
    public boolean existsWithPrimaryKey(final PK anKey) {
        return mapper.existsWithPrimaryKey(anKey);
    }

    /**
     * 构建Example
     * @return
     */
    protected Example buildExample() {
        final Example example = new Example(findGenericClass());
        return example;
    }

    /**
     * 查询所有记录
     * @return 查询结果
     */
    protected List<T> selectList() {
        return this.mapper.selectAll();
    }

    /**
     * 根据条件查询，获取第一条记录
     * @param example
     * @return
     */
    protected T selectFirstOneByExample(final Example example) {
        final List<T> list = this.selectByExampleAndLimit(example, 0, 1);
        if (CollectionUtils.isEmpty(list)) {
            return null;
        }
        return list.get(0);
    }

    /**
     * 根据Example条件查询数据，定义区间段
     * @param example 条件
     * @param offset  开始位置
     * @param limit   数量
     * @return
     */
    protected List<T> selectByExampleAndLimit(final Example example, final int offset, final int limit) {
        return this.mapper.selectByExampleAndRowBounds(example, new RowBounds(offset, limit));
    }

    /**
     * 根据条件查询列表
     * @param example
     * @return
     */
    protected List<T> selectByExample(final Example example) {
        return this.mapper.selectByExample(example);
    }

    /**
     * 根据条件查询数据条数
     * @param example
     * @return
     */
    protected int selectCountByExample(final Example example) {
        if (Objects.isNull(example)) {
            return -1;
        }
        return this.mapper.selectCountByExample(example);
    }

    /**
     * 分页查询
     * @param pageRequest 分页对象
     * @param example    条件
     * @return
     */
    protected PageResult<T> selectPage(final PageRequest pageRequest, Example example) {
        return this.selectPage(pageRequest, example, true);
    }

    /**
     * 分页查询
     * @param pageRequest 分页对象
     * @param example    条件
     * @param count      是否统计总数
     * @return
     */
    protected PageResult<T> selectPage(final PageRequest pageRequest, Example example, boolean count) {
        final int offset = (pageRequest.getPage() - 1) * pageRequest.getSize();
        final List<T> list = this.selectByExampleAndLimit(example, offset, pageRequest.getSize());
        final PageResult pageResult = PageResult.of(list);
        if (count) {
            final long total = this.selectCountByExample(example);
            pageResult.setTotal(total);
        }
        return pageResult;
    }

    /**
     * 查询分页List
     * @param pageRequest 分页对象
     * @param example 条件
     * @return
     */
    protected List<T> selectPageList(final PageRequest pageRequest, Example example) {
        final int offset = (pageRequest.getPage() - 1) * pageRequest.getSize();
        return this.selectByExampleAndLimit(example, offset, pageRequest.getSize());
    }

    /**
     * 根据条件更新
     * @param example
     * @return
     */
    protected int deleteByExample(final Example example) {
        example.setForUpdate(true);
        return this.mapper.deleteByExample(example);
    }


    /**
     * 创建数据保存数据之前额外操作回调方法 默认为空逻辑，子类根据需要覆写添加逻辑即可
     * @param entity 待创建数据对象
     */
    protected void preInsert(final T entity) {
    }

    /**
     * 更新数据保存数据之前额外操作回调方法 默认为空逻辑，子类根据需要覆写添加逻辑即可
     * @param entity 待更新数据对象
     */
    protected void preUpdate(final T entity) {
    }

    /**
     * 查询类的泛型类
     * @return 泛型类
     */
    @SuppressWarnings("unchecked")
    protected Class<T> findGenericClass() {
        if (Objects.isNull(entityClass)) {
            synchronized (this) {
                if (Objects.isNull(entityClass)) {
                    try {
                        // 通过反射取得Entity的Class.
                        final Object genericClass = getClass().getGenericSuperclass();
                        if (genericClass instanceof ParameterizedType) {
                            this.entityClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass())
                                    .getActualTypeArguments()[1];
                            return this.entityClass;
                        }
                    } catch (final Exception e) {
                        log.error("error detail:", e);
                    }
                    throw new SystemErrorException("泛型定义没有找到");
                }
            }
        }
        return this.entityClass;
    }

    /**
     * 查询所有
     * @return 对象List
     */
    public List<T> selectAll() {
        return this.mapper.selectAll();
    }
}
